#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <time.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>

#include "csocket.h"
#include "global.h"
#include "memory.h"
#include "funcutil.h"

CSocket::CSocket() :
    m_maxWorkerNum(1),
    m_listenPortNum(1),
    m_epollfd(1),
    m_pConnections(nullptr),
    m_pHeadFreeConnections(nullptr),
    m_pktHeaderLen(sizeof(PKT_HEADER)),
    m_msgHeaderLen(sizeof(MSG_HEADER)),
    m_recvMsgQueueSize(0)
{
    pthread_mutex_init(&m_recvMsgQueueMutex, nullptr);
}

CSocket::~CSocket()
{
    //1. 监听端口的释放
    for (auto it = m_listenSocketList.begin(); it != m_listenSocketList.end(); ++it)
    {
        delete (*it); //由于是被new出来的，需要delete
    }
    m_listenSocketList.clear();

    //2. 释放连接池相关资源（待补充）
    //TODO
    if (m_pConnections != nullptr)
        delete [] m_pConnections; //由于m_pConnections是new出来的，需要delete

    //3. 释放接收消息队列中的资源
    clearMsgRecvQueue();

    //4. pthread资源释放
    pthread_mutex_destroy(&m_recvMsgQueueMutex);
}

bool CSocket::Initialize()
{
    ReadConf();
    return openListeningSockets();
}

void CSocket::ReadConf()
{
    //TODO
    return ;
}

bool CSocket::openListeningSockets()
{
    int         sockfd;
    int         portnum = 9000; //暂时写死，后续可以读配置文件获取
    char        buf[100] = {0}; //临时字符串
    struct sockaddr_in serverAddr; //服务端地址结构体

    //初始化端口监听
    memset(&serverAddr, 0, sizeof(serverAddr));
    serverAddr.sin_family = AF_INET; //选择IPv4
    serverAddr.sin_addr.s_addr = htonl(INADDR_ANY); //监听所有地址

    for (int i = 0; i < m_listenPortNum; ++i)
    {
        sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (sockfd == -1)
        {
            LOGSTDERR(errno, "CSocket::openListeningSockets() socket() error");
            return false;
        }

        int REUSEADDR = 1;
        if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const void*)&REUSEADDR, sizeof(REUSEADDR)) == -1)
        {
            LOGSTDERR(errno, "CSocket::openListeningSockets() setsockopt() error");
            return false;
        }

        if (setNonBlocking(sockfd) == false)
        {
            LOGSTDERR(errno, "CSocket::openListeningSockets() setNonBlocking() error");
            return false;
        }

        //一路走到这都没问题就可以开始绑定端口了
        if (bind(sockfd, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) == -1)
        {
            LOGSTDERR(errno, "CSocket::openListeningSockets() bind() error");
            close(sockfd); //致命错误直接关了
            return false;
        }
        //监听
        if (listen(sockfd, LISTEN_BACKLOG) == -1)
        {
            LOGSTDERR(errno, "CSocket::openListeningSockets() listen() error");
            close(sockfd); //致命错误直接关了
            return false;
        }

        pSListen ptemp = new SListenPort(portnum, sockfd); //调用new带参构造
        // ptemp->connection 这个暂时还用不到
        //至此已经监听端口成功，可以输出日志消息
        m_listenSocketList.push_back(ptemp); //将监听的端口放入监听队列中

    }

    //if (m_listenSocketList.size() <= 0) return false; //这个情况不太可能发生
    return true;
}

bool CSocket::setNonBlocking(int sockfd)
{
    int NON_BLOCK = 1;
    if (ioctl(sockfd, FIONBIO, &NON_BLOCK) == -1)
    {
        return false;
    }
    //第二种设置NONBLOCK的方式，
    //int opts = fcntl(sockfd, F_GETFL);
    //if (opts < 0)
    //{
    //    LOGSTDERR(errno, "CSocket::setNonBlocking(int sockfd) fcntl(F_GETFL) error");
    //    return false;
    //}
    //opts |= O_NONBLOCK;
    //if (fcntl(sockfd, F_SETFL, opts) < 0)
    //{
    //    LOGSTDERR(errno, "CSocket::setNonBlocking(int sockfd) fcntl(F_SETFL) error");
    //    return false;
    //}

    return true;
}

void CSocket::closeListeningSockets()
{
    for (int i = 0; i < m_listenPortNum; ++i)
    {
        close(m_listenSocketList[i]->fd);
        LOGSTDERR();
    }
    return ;
}

size_t CSocket::sock_ntop(struct sockaddr* sa, int port, char* text, size_t len)
{
    struct sockaddr_in*     sin;
    char*                   p;

    switch (sa->sa_family)
    {
    case AF_INET:
        sin = (struct sockaddr_in *) sa;
        p = (char *) &sin->sin_addr;
        if (port)
        {
            p = wb::snprintf(text, len, "%ud.%ud.%ud.%ud:%d", p[0], p[1], p[2], p[3], ntohs(sin->sin_port));
        }
        else
        {
            p = wb::snprintf(text, len, "%ud.%ud.%ud.%ud", p[0], p[1], p[2], p[3]);
        }
        return (p - text);
        break;
    default:
        break;
    }

    return 0;
}

int CSocket::epollInit()
{
    //1. 创建epoll对象
    m_epollfd = epoll_create(m_maxWorkerNum);
    if (m_epollfd == -1)
    {
        LOGSTDERR();
        exit(2);
    }

    //2. 创建连接池数组（用来处理所有客户端连接）
    m_curConnectionNum = m_maxWorkerNum;
    m_pConnections = new SConnection[m_curConnectionNum];

    int i = m_maxWorkerNum;
    pSConnection data = nullptr; //链表尾部
    pSConnection conn = m_pConnections; //链表指针
    do {
        --i;
        //从后向前头插法构造链表
        conn[i].next = data;
        conn[i].fd = -1;
        conn[i].instance = 1;
        conn[i].iCurSeq = 0; //序列号统一从0开始
        //设置好数据后，data游标往前移动
        data = &conn[i];
    } while (i);

    m_pHeadFreeConnections = data; //出while循环时，data指向的是数组第一个
    m_freeConnectionNum = m_curConnectionNum;

    //遍历所有的监听端口列表
    for (auto it = m_listenSocketList.begin(); it != m_listenSocketList.end(); ++it)
    {
        conn = getConnection((*it)->fd); //传入一个套接字，从连接池中获取一个空闲连接对象指针
        if (conn == nullptr)
        {
            LOGSTDERR();
            exit(2); //致命问题，需要退出
        }
        conn->listening = (*it); //获取的空闲连接对象内的 监听结构体->指向it所指的监听端口，做绑定
        (*it)->connection = conn; //监听端口列表中的监听端口结构体内的 连接结构体指针->指向空闲连接，做绑定
        //至此，一个空闲连接与列表中的监听套接字做了绑定
        //同时，这个套接字也绑定了一个空闲的连接

        conn->rdHandle = &CSocket::eventAccept; //绑定

        //向epoll对象中添加监听端口（fd），使得后面可以用epoll_wait()对事件进行关注
        if (epollAddEvent((*it)->fd,
                          1, 0,             //只关心可读事件，不关心可写事件
                          0,                //其他标记暂无
                          EPOLL_CTL_ADD,    //事件类型：添加事件
                          conn
                          ) == -1)
        {
            exit(2);
        }
    }
    return 0;
}

int CSocket::epollAddEvent(int sockfd, int rdevt, int wrevt, uint32_t oflag,
                           uint32_t etype, pSConnection c)
{
    struct epoll_event ev;
    memset(&ev, 0, sizeof(ev));

    if (rdevt == 1)
    {
        ev.events = EPOLLIN | EPOLLRDHUP; //监控 可读事件 和 对端关闭事件 默认是LT水平触发
    }
    else
    {
        //TODO其他事件后续添加
    }

    ev.events |= oflag; //按位或oflag其他标志位

    //由于指针的最低位肯定不是1，所以可以利用这个特性让指针多记录一个状态
    //这个instance状态可以用来标记事件是否有效
    ev.data.ptr = (void*)( (uintptr_t)c | c->instance);

    if (epoll_ctl(m_epollfd, etype, sockfd, &ev) == -1)
    {
        LOGSTDERR(errno, "CSocket::epollAddEvent() error");
        return 0;
    }

    return 1;
}

int CSocket::epollProcessEvents(int timer)
{
    //等待事件发生，发生的事件会返回到m_events数组里，做多返回MAX_EVENTS个事件
    //返回值代表捕获了那么多个事件，返回0表示超时未捕获到事件
    int events = epoll_wait(m_epollfd, m_events, MAX_EVENTS, timer);
    if (events == -1)
    {
        //返回-1表示有错误发生
        //EINTR错误表示阻塞等待期间，收到了信号量，导致系统中断，但是一般情况下不会给woker进程发信号，
        if (errno == EINTR)
        {
            LOGSTDERR();
            return 1;
        }
        else
        {
            //如果不是EINTR错误，可能是其他错误，记录一下日志
            LOGSTDERR(errno, "CSocket::epollProcessEvents() epoll_wait() error");
            return 0; //非正常返回
        }
    }

    if (events == 0)
    {
        //等待超时
        if (timer != -1)
        {
            //正常未等到事件
            return 1;
        }
        //timer为-1却没等待到事件，程序运行到这说明有问题，记录日志
        LOGSTDERR(errno, "ALERT CSocket::epollProcessEvents() epoll_wait() timeout unexpected");
        return 0;
    }
    //至此开始处理事件
    pSConnection conn; //连接结构体指针
    uintptr_t       instance; //提取标志位
    uint32_t        eventType; //epoll事件类型
    for (int i = 0; i < events; ++i) //遍历events个事件，挨个处理
    {
        conn = (pSConnection)(m_events[i].data.ptr); //注意，在添加epoll事件的时候，把这个指针特殊处理了
        instance = (uintptr_t) conn & 1; //获取特殊指针的最后一位标志位
        conn = (pSConnection)( (uintptr_t) conn & (uintptr_t) ~1); //将特殊指针恢复成正常指针

        if (conn->fd == -1)
        {
            //假设epoll_wait取得了3个事件，处理的时候是有先后顺序的
            //如果第1个事件把连接关闭了，连接的fd被置为-1了，紧接着来了第2第3个事件
            //碰巧第3个事件的服用了第1个事件连接关闭时释放的那个连接，就会出现fd为-1的情况
            //这种情况是属于过期事件
            LOGSTDERR(errno, "CSocket::epollProcessEvents() meets expired event");
            continue;
        }

        if (conn->instance != instance)
        {
            //过滤掉失效事件
            LOGSTDERR(errno, "CSocket::epollProcessEvents() meets expired event");
            continue;
        }

        eventType = m_events[i].events;
        if (eventType & (EPOLLERR | EPOLLHUP))
        {
            //如果发生错误 或者对端关闭连接
            eventType |= (EPOLLIN | EPOLLOUT); //把事件类型加上期望可写事件，因为是LT模式，这个事件会被持续触发，在下一次循环的时候再次进入处理阶段，做可读事件的处理
        }

        if (eventType & EPOLLIN)
        {
            //如果是可读事件
            //调用rdHandle 如果是新连接，此处调用的是epollInit()中定义的eventAccept
            //否则调用的是waitRequestHandle
            (this->*(conn->rdHandle))(conn);
        }

        if (eventType & EPOLLOUT)
        {
            //客户端关闭时会执行到这里，由于第一个if语句添加的EPOLLOUT
            //TODO:处理逻辑后续再添加...
            LOGSTDERR(errno, "EPOLLOUT");
        }
    }
    return 1; //正常返回
}